#include "LogicSystem.h"
#include <boost/beast/http.hpp>
#include <boost/beast.hpp>
#include <boost/asio.hpp>
#include "HttpConnection.h"
#include "const.h"

#include "VarifyGrpcClient.h"
#include "RedisMgr.h"
#include "MysqlMgr.h"
#include "StatusGrpcClient.h"



LogicSystem::~LogicSystem()
{

}

bool LogicSystem::HandleGet(std::string path, std::shared_ptr<HttpConnection> con)
{
	if (_get_handlers.find(path) == _get_handlers.end()) {
		return false;
	}

	_get_handlers[path](con);
	return true;

}

bool LogicSystem::HandlePost(std::string path, std::shared_ptr<HttpConnection> con) 
{
	if (_post_handlers.find(path) == _post_handlers.end()) 
	{
		return false;
	}

	_post_handlers[path](con);
	return true;
}



void LogicSystem::RegGet(std::string url, HttpHandler handler)
{
	_get_handlers.insert(make_pair(url, handler));

}

void LogicSystem::RegPost(std::string url, HttpHandler handler) 
{
	_post_handlers.insert(make_pair(url, handler));
}

LogicSystem::LogicSystem() {

	//参数都已经被及西到对应的connect中，框架性得到大幅度提升，我们只要做url----->函数映射就行（只要做url---->对应的函数对象无参数）
	//里面涉及到具体的操作的时候，参数已经被解析到connection中，就可以另外写逻辑，也就是说，把conection传过来之后，相当于把所有的参数带古来了
	//逻辑系统就不必为了单独的connect去开发协同不一样的函数对象
	//当然也是可以使用不定参数函数模板来解决，比如线程池技术那样
	//但是那样有什么弊端？也没有弊端，付出的成本几乎一致，一个是修改内部的调用逻辑，加上，一个是加一个映射，然后传参，emmmm，似乎没有多大的区别
	RegGet("/get_test", [](std::shared_ptr<HttpConnection> connection) {
		beast::ostream(connection->_response.body()) << "receive get_test req\n";
		//在这里处理具体的要参数的业务，上一层统一架构，
		int i = 0;
		for (auto& elem : connection->_get_params) 
		{ 
			i++;
			beast::ostream(connection->_response.body()) << "param" << i << " key is " << elem.first;
			beast::ostream(connection->_response.body()) << ", " << " value is " << elem.second << std::endl;
		}
	});

	//注册post函数，后端处理逻辑，前端发送过来，后端解析
	//前端发送过来一个json格式的文本
	//Get所有的参数都是在URL中一起过来
	//post所有的参数是通过json发送过来的
	RegPost("/get_varifycode", [](std::shared_ptr<HttpConnection> connection)->bool{
		auto body_str = boost::beast::buffers_to_string(connection->_request.body().data());
		std::cout << "receive body is " << body_str << std::endl;
		connection->_response.set(http::field::content_type, "text/json");		//告诉对方返回的文本类型
		Json::Value root;														//序列化使用
		Json::Reader reader;													//反序列化使用
		Json::Value src_root;													//反序列化使用
		bool parse_success = reader.parse(body_str, src_root);					//反序列化
		if (!parse_success) {
			std::cout << "Failed to parse JSON data!" << std::endl;
			root["error"] = ErrorCodes::Error_Json;								//序列化，准备发送回去
			
			std::string jsonstr = root.toStyledString();						//序列化
			beast::ostream(connection->_response.body()) << jsonstr;			//写入回应体
			return true;
		}
																				//解析成功后，可以通过json格式读取出来
		auto email = src_root["email"].asString();								//反序列化
		GetVarifyRsp rsp = VerifyGrpcClient::GetInstance()->GetVarifyCode(email);		//GRPC远程调用
		cout << "email is " << email << endl;
		root["error"] = 0;														//序列化
		root["email"] = src_root["email"];										//序列化
		std::string jsonstr = root.toStyledString();							
		beast::ostream(connection->_response.body()) << jsonstr;
		return true;
		});


	//注册post处理函数
	RegPost("/user_register", [](std::shared_ptr<HttpConnection> connection) {

		//
		auto body_str = boost::beast::buffers_to_string(connection->_request.body().data());
		std::cout << "receive body is " << body_str << std::endl;
		connection->_response.set(http::field::content_type, "text/json");					//设置返回的是json类型的信息
		Json::Value root;																	//待会response的json，准备用于序列化的
		Json::Reader reader;
		Json::Value src_root;																//反向序列化节点
		bool parse_success = reader.parse(body_str, src_root);								//反序列化,将require消息反序列化到一个json格式的字符串
		if (!parse_success) {
			std::cout << "Failed to parse JSON data!" << std::endl;
			root["error"] = ErrorCodes::Error_Json;											//设置json格式翁本，准备用于序列化
			std::string jsonstr = root.toStyledString();									//序列化
			beast::ostream(connection->_response.body()) << jsonstr;						//将序列化字符写入response
			return true;
		}
		//先查找redis中email对应的验证码是否合理
		std::string  varify_code;															
		bool b_get_varify = RedisMgr::GetInstance()->Get(CODEPREFIX +src_root["email"].asString(), varify_code);
		if (!b_get_varify) {
			std::cout << " get varify code expired" << std::endl;
			root["error"] = ErrorCodes::VarifyExpired;
			std::string jsonstr = root.toStyledString();
			beast::ostream(connection->_response.body()) << jsonstr;
			return true;
		}

		if (varify_code != src_root["varifycode"].asString()) {
			std::cout << " varify code error" << std::endl;
			root["error"] = ErrorCodes::VarifyCodeErr;
			std::string jsonstr = root.toStyledString();
			beast::ostream(connection->_response.body()) << jsonstr;
			return true;
		}
		//上面：根据邮箱--->验证码
		//这个是：通过Mysql去查找用户是否存在，而不是通过redis
		//bool b_usr_exist = RedisMgr::GetInstance()->ExistsKey("code_"+src_root["user"].asString());
		//if (b_usr_exist) {
		//	std::cout << " user exist" << std::endl;
		//	root["error"] = ErrorCodes::UserExist;
		//	std::string jsonstr = root.toStyledString();
		//	beast::ostream(connection->_response.body()) << jsonstr;
		//	return true;
		//}

		//查询数据库，是否存在用户信息


		//连接数据库，注册用户
		//这条语句里会兼容查询是否有相同的名字或者相同的邮箱的人，这个函数负责了太多的职责，我觉得不好
		//我觉得应该把查询相同的人，查询相同的邮箱的职责分出去
		//像现在这样做，客户端那里就没有了很多信息
		MysqlMgr::GetInstance()->RegUser(src_root["user"].asCString(),src_root["email"].asCString(),src_root["passwd"].asCString(),"");			//注册信息



		//经过以上查找，确认：存在用户，验证码正确，所以能够正常注册
		root["error"] = 0;
		root["email"] = src_root["email"].asCString();
		root["user"] = src_root["user"].asString();
		root["passwd"] = src_root["passwd"].asString();
		root["confirm"] = src_root["confirm"].asString();
		root["varifycode"] = src_root["varifycode"].asString();
		std::string jsonstr = root.toStyledString();
		beast::ostream(connection->_response.body()) << jsonstr;
		return true;
		});


	//初测修改密码的业务服务
	RegPost("/user_resetpasswd", [](std::shared_ptr<HttpConnection> connection) {
		
		//lambda匿名可调用对象，不需要使用binder，支持异步调用
		std::cout << "收到修改信息" << std::endl;
		//消息转换成字符串--->反序列化--参数解析
		
		auto bodyStr = boost::beast::buffers_to_string(connection->_request.body().data());
		connection->_response.set(http::field::content_type, "text/json");					//设置回复消息类型是json类型
		Json::Value root;					//重新组成使用,不一定要有这个，这个是待会要去重新组装一个json要使用
		Json::Reader reader;				//读
		Json::Value src_root;				//解析使用
		bool parse_success = reader.parse(bodyStr, src_root);

		//无法解析也要返回回去，待会客户端也要对回来的消息进行解析，约定发送回去的消息带一个error信息
		//客户端那里，由于ReqID 和MOD的原因会精准的发送对应的模块进行处理，所以服务端这里就不用考虑通用性，直接更具客户端的业务去定制
		//想要发送的消息就行，不要考虑通用新
		if (parse_success)
		{
			std::string cc;
			root["error"] = ErrorCodes::Success;
			//凭借待会要冲洗你发送回去的JSOn
			root["user"] = src_root["user"].asString();
			cc = src_root["user"].asString();
			root["message_email"] = src_root["email"].asString();
			cc = src_root["email"].asString();
			root["new_passwd"] = src_root["newpasswd"].asString();
			cc = src_root["newpasswd"].asString();
			//std::cout << src_root["newpasswd"].asString() << std::endl;
			//序列化，写入回复消息
			std::string jsonstr = root.toStyledString();
			beast::ostream(connection->_response.body()) << jsonstr;


			//以后的一些业务，包括查询到数据库然后进行修改密码
			//根据用户查询到对应的密码，然后修改密码

			//也可以重新发送验证码过来，后端这里远程调用
			//加上检验验证码的逻辑
			//加上检验邮箱的逻辑，重新发送一次验证码之类的过去更新
			//加上检验是否两次密码一致的逻辑

			MysqlMgr::GetInstance()->UpdatePwd(src_root["user"].asString(), src_root["newpasswd"].asString());

			return true;
		}
		else
		{
			root["error"] = ErrorCodes::Error_Json;
			std::cout << "解析JOn参数失败" << std::endl;
			return true;
		}

		});

	//添加登录服务功能
	RegPost("/user_login", [](std::shared_ptr<HttpConnection> connection) {

		//lambda匿名可调用对象，不需要使用binder，支持异步调用
		std::cout << "收到登录信息" << std::endl;
		//消息转换成字符串--->反序列化--参数解析

		auto bodyStr = boost::beast::buffers_to_string(connection->_request.body().data());
		connection->_response.set(http::field::content_type, "text/json");					//设置回复消息类型是json类型
		Json::Value root;					//重新组成使用,不一定要有这个，这个是待会要去重新组装一个json要使用
		Json::Reader reader;				//读
		Json::Value src_root;				//解析使用
		bool parse_success = reader.parse(bodyStr, src_root);

		//无法解析也要返回回去，待会客户端也要对回来的消息进行解析，约定发送回去的消息带一个error信息
		//客户端那里，由于ReqID 和MOD的原因会精准的发送对应的模块进行处理，所以服务端这里就不用考虑通用性，直接更具客户端的业务去定制
		//想要发送的消息就行，不要考虑通用新

		if (!parse_success) {
			std::cout << "Failed to parse JSON data!" << std::endl;
			root["error"] = ErrorCodes::Error_Json;
			std::string jsonstr = root.toStyledString();
			beast::ostream(connection->_response.body()) << jsonstr;
			return true;
		}
		else
		{
			auto email = src_root["email"].asString();
			auto pwd = src_root["passwd"].asString();

			UserInfo user;
			bool pwd_valid=MysqlMgr::GetInstance()->CheckPwd(email, pwd, user);		//连接数据库，检查密码

			if (!pwd_valid) {
				std::cout << " user pwd not match" << std::endl;
				root["error"] = ErrorCodes::PasswdInvalid;
				std::string jsonstr = root.toStyledString();
				beast::ostream(connection->_response.body()) << jsonstr;
				return true;
			}

			//连接状态服务器,这个待会再写
			auto reply = StatusGrpcClient::GetInstance()->GetChatServer(user.uid);
			if (reply.error()!=ErrorCodes::Success) {
				std::cout << " grpc get chat server failed, error is " << reply.error() << std::endl;
				root["error"] = ErrorCodes::RPCFailed;
				std::string jsonstr = root.toStyledString();
				beast::ostream(connection->_response.body()) << jsonstr;
				return true;
			}

			std::cout << "succeed to load userinfo uid is " << user.uid << std::endl;
			root["error"] = ErrorCodes::Success;
			root["email"] = email;
			root["uid"] = user.uid;
			root["token"] = reply.token();
			root["host"] = reply.host();
			root["port"] = reply.port();
			std::string jsonstr = root.toStyledString();
			beast::ostream(connection->_response.body()) << jsonstr;
			return true;
		}
		});




}
